package com.mspbots.attendance.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mspbots.attendance.controller.AttendanceController;
import com.mspbots.attendance.enums.AttendanceTypeEnum;
import com.mspbots.attendance.mapper.AttendanceMapper;
import com.mspbots.attendance.model.Attendance;
import com.mspbots.attendance.service.AttendanceService;
import com.mspbots.entry.SenderMessageBody;
import com.mspbots.entry.UserMappingResponse;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * @author billb
 */
@Log4j2
@Service
public class AttendanceServiceImpl extends ServiceImpl<AttendanceMapper, Attendance>
        implements AttendanceService {

    @Override
    public Attendance findLogLast(AttendanceController.AttendanceParams lastParams) {
        return this.getOne(Wrappers.<Attendance>lambdaQuery()
                .allEq(lastParams.toMap(), false)
                .between(!ObjectUtils.isEmpty(lastParams.getStartTime()), Attendance::getLogTime,
                        ObjectUtils.isEmpty(lastParams.getStartTime()) ? LocalDateTime.now() : lastParams.getStartTime(),
                        ObjectUtils.isEmpty(lastParams.getEndTime()) ? LocalDateTime.now() : lastParams.getEndTime())
                .orderByDesc(Attendance::getId).last("limit 1"));
    }

    @Override
    public List<Attendance> selectList(AttendanceController.AttendanceParams lastParams) {
        List<Attendance> result = this.list(Wrappers.<Attendance>lambdaQuery()
                .allEq(lastParams.toMap(), false)
                .between(!ObjectUtils.isEmpty(lastParams.getStartTime()), Attendance::getLogTime,
                        ObjectUtils.isEmpty(lastParams.getStartTime()) ? LocalDateTime.now() : lastParams.getStartTime(),
                        ObjectUtils.isEmpty(lastParams.getEndTime()) ? LocalDateTime.now() : lastParams.getEndTime())
                .orderByDesc(Attendance::getId));
        return ObjectUtils.isEmpty(result) ? new ArrayList<>() : result;
    }

    @Override
    public List<Attendance> allUserDay(AttendanceController.AttendanceParams params) {
        Assert.notNull(params.getTenantId(), "Tenant id is can not be empty or null.");
        Long tenantId = params.getTenantId();
        Integer type = 0;
        if (params.getType() != null) {
            type = params.getType().getValue();
        }
        Long departmentId = params.getDepartmentId();
        return this.baseMapper.findUserDay(tenantId, type, departmentId);
    }

    @Override
    public Attendance attendance(UserMappingResponse mapping, AttendanceTypeEnum type) {
        return this.attendance(mapping, type, SenderMessageBody.builder()
                .logTime(LocalDateTime.now(ZoneOffset.of(mapping.getTz()))).build());
    }

    @Override
    public Attendance attendance(UserMappingResponse mapping, AttendanceTypeEnum type,
                                 SenderMessageBody body) {
        Attendance attendance = Attendance.builder().build();
        attendance.setTenantId(mapping.getTenantId());
        attendance.setTenantUserId(mapping.getTenantUserId());
        attendance.setTeamsUserId(mapping.getTeamsUserId());
        attendance.setType(type);
        attendance.setFromType(body.getFromType());
        attendance.setLogTime(body.getLogTime());
        attendance.setReason(body.getReason());
        attendance.setDuration(body.getDuration());
        attendance.setTz(mapping.getTz());
        attendance.setTzStr(mapping.getTzStr());
        this.saveOrUpdate(attendance);
        return attendance;
    }


    @Override
    public Attendance attendanceExists(UserMappingResponse mapping) {
        LocalDateTime nowTime = LocalDateTime.now();
        return this.getOne(Wrappers.<Attendance>lambdaQuery()
                .eq(Attendance::getTenantId, mapping.getTenantId())
                .eq(Attendance::getTenantUserId, mapping.getTenantUserId())
                .eq(Attendance::getTeamsUserId, mapping.getTeamsUserId())
                .between(Attendance::getCreatedTime, nowTime.minusHours(12)
                                .format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
                        nowTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
                .orderByDesc(Attendance::getId).last("limit 1"));
    }

    @Override
    public Attendance save(AttendanceController.AttendanceParams lastParams) {
        Attendance attendance = Attendance.builder()
                .teamsUserId(lastParams.getTeamsId())
                .type(lastParams.getType())
                .logTime(lastParams.getLogTime())
                .logTimeUtc(lastParams.getLogTime().plusHours(ZoneOffset.of(lastParams.getTz()).getTotalSeconds() / 60 / 60))
                .tz(lastParams.getTz())
                .tzStr(lastParams.getTzStr())
                .fromType(lastParams.getFromType())
                .tenantUserId(lastParams.getUserId()).build();
        attendance.setTenantId(lastParams.getTenantId());
        attendance.setId(lastParams.getId());
        super.saveOrUpdate(attendance);
        return attendance;
    }

    @Override
    public IPage<Attendance> page(IPage<Attendance> page, AttendanceController.AttendanceParams lastParams) {
        return super.page(page, toQueryWrapper(lastParams));
    }

    private QueryWrapper<Attendance> toQueryWrapper(AttendanceController.AttendanceParams params) {
        QueryWrapper<Attendance> queryWrapper = new QueryWrapper<>();
        queryWrapper.allEq(params.toStrMap(), false);
        queryWrapper.between(!ObjectUtils.isEmpty(params.getStartTime()), "attendance.log_time_utc",
                ObjectUtils.isEmpty(params.getStartTime()) ? LocalDateTime.now() : params.getStartTime(),
                ObjectUtils.isEmpty(params.getEndTime()) ? LocalDateTime.now() : params.getEndTime());
        return queryWrapper;
    }
}
